package org.jiangy.usercenter;

import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.jiangy.authentication.DefaultGrantedAuthority;
import org.jiangy.authentication.SessionHelper;
import org.jiangy.usercenter.entity.*;
import org.jiangy.usercenter.repository.UserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;

/**
 * <p>创建时间: 2024/8/12 </p>
 *
 * @author <a href="mailto:jiangliu0316@outlook.com" rel="nofollow">蒋勇</a>
 */
@Slf4j
@RestController
@RequestMapping
public class UserController {

    private final UserRepository repository;
    private final PasswordEncoder passwordEncoder;
    private final UserDetailsManager userDetailsManager;
    private final HttpServletRequest request;

    @Value("${security.issuer-uri}")
    private String issuerUri;

    public UserController(UserRepository repository,
                          PasswordEncoder passwordEncoder,
                          UserDetailsManager userDetailsManager,
                          HttpServletRequest request) {
        this.repository = repository;
        this.passwordEncoder = passwordEncoder;
        this.userDetailsManager = userDetailsManager;
        this.request = request;
    }

    @GetMapping("/userinfo")
    public UserInfo getUserInfo() {
        User user = SessionHelper.getUserInfo().orElseThrow();
        UserInfo userInfo = null;
        if (user instanceof Customer customer) {
            userInfo = new UserInfo(customer.getId(),
                    customer.getUsername(),
                    customer.isEnabled(),
                    customer.getAvatar(),
                    customer.getAuthorities(),
                    customer.getRealName(),
                    customer.isSupplier(),
                    customer.isReseller(),
                    customer.getLockedDeposit(),
                    customer.getSupplierInfo(),
                    customer.getResellerInfo()
            );
        }
        if (user instanceof Staff staff) {
            userInfo = new UserInfo(staff.getId(),
                    staff.getUsername(),
                    staff.isEnabled(),
                    staff.getAvatar(),
                    staff.getAuthorities(),
                    staff.getRealName(),
                    false,
                    false,
                    null,
                    null,
                    null
            );
        }
        return userInfo;
    }

    /**
     * 创建一个员工用户
     *
     * @param createUserReq 创建用户请求
     */
    @PostMapping("/users")
    public void create(@RequestBody CreateStaffReq createUserReq) {
        Staff user = new Staff();
        user.setType(UserTypeEnum.STAFF);
        user.setUsername(createUserReq.username());
        user.setPassword(createUserReq.password());
        user.setSuperAdmin(false);
        repository.save(user);
    }

    /**
     * 注册用户
     * // TODO: 处理注册相关异常
     * // TODO: 记录注册用户 ip
     */
    @PostMapping("/auth/signup")
    public String registerUser(@RequestBody CreateCustomerReq createCustomerReq) {
        Supplier supplier = Supplier.builder()
                .allowSale(true)
                .customerServicePhones("18580687918")
                .customerServiceQqs("778671998")
                .storeName("百度供应商")
                .storeUrl("https://www.baidu.com")
                .build();

        Customer user = new Customer();
        user.setType(UserTypeEnum.CUSTOMER);
        user.setUsername(createCustomerReq.username());
        user.setPassword(passwordEncoder.encode(createCustomerReq.password()));
        user.setEnabled(true);
        user.setAccountNonExpired(true);
        user.setAccountNonLocked(true);
        user.setCredentialsNonExpired(true);
        user.setSupplierInfo(supplier);

        // 额外信息
        user.setRegisteredAt(Instant.now());
        user.setRegisteredIp(request.getRemoteAddr());

        supplier.setCustomer(user);

        userDetailsManager.createUser(user);
        // 注册成功，自动登录
        if (Objects.equals(createCustomerReq.isLoginNow(), true)) {
            return internalLogin(user.getUsername(), createCustomerReq.password());
        }
        return "";
    }

    @GetMapping("/users/{id}")
    public Optional<User> findById(@PathVariable Long id) {
        return repository.findById(id);
    }

    /**
     * 内部登录
     */
    @Nullable
    private String internalLogin(String username, String password) {
        try {
            var client = HttpClient.newHttpClient();
            HttpRequest httpRequest = HttpRequest.newBuilder()
                    .uri(URI.create(issuerUri + "/auth/login"))
                    .header("Content-Type", "application/x-www-form-urlencoded")
                    .POST(HttpRequest.BodyPublishers
                            .ofString(String.format("username=%s&password=%s",
                                    username, password)))
                    .build();
            HttpResponse<String> response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
            // FIXME: 需要解析 OAuthError 异常，并告知前端下一步该如何做，例如弹出登录表单。
            return response.body();
        } catch (IOException | InterruptedException e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * "id": "0001",
     * "username": "user",
     * "source": "CUSTOMER",
     * "enabled": true,
     * "authorities": [
     * {
     * "authority": "ROLE_USER"
     * }
     * ],
     * "realName": "jiangy",
     * "supplier": true,
     * "reseller": false,
     * "lockedDeposit": 0.88
     */
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public record UserInfo(Long id, String username, boolean enabled, String avatar,
                           Collection<DefaultGrantedAuthority> authorities, String realName, boolean supplier,
                           boolean reseller, BigDecimal lockedDeposit, Supplier supplierInfo, Reseller resellerInfo) {
    }

    public record CreateStaffReq(String username, String password) {
    }

    public record CreateCustomerReq(String username, String password, Boolean isLoginNow) {
    }
}
